﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Volo.Abp;

namespace Xms.Abp.Core;

public static class DynamicConditionExtensions
{
    //public static Expression<Func<T, bool>> BuildExpression<T>(this IEnumerable<IDynamicConditionValue> conditions)
    //{
    //    Expression<Func<T, bool>> where = r => true;
    //    if (!conditions.Any()) return @where;
    //    foreach (var condition in conditions)
    //    {
    //        var lambda = condition.Build<T>();
    //        @where = @where.And(lambda);
    //    }
    //    return @where;
    //}

    public static Expression<Func<T, bool>> Build<T>(this DynamicConditionValue condition)
    {
        try
        {
            var field = condition.Field;
            var compare = condition.CompareType;
            var type = condition.CompareDataType;
            var value = condition.Value;
            if (string.IsNullOrEmpty(field))
                throw new UserFriendlyException("无法构建查询条件");
            if (compare != CompareType.Empty && compare != CompareType.NotEmpty && compare != CompareType.HasValue && compare != CompareType.NotHasValue && string.IsNullOrEmpty(value))
                throw new UserFriendlyException("无法构建查询条件");
            //构建Lambda表达式
            var parameter = Expression.Parameter(typeof(T), "p");
            Expression constant;
            //表达式左侧 like: p.Name
            var left = Expression.PropertyOrField(parameter, field);
            //比较表达式
            switch (compare)
            {
                case CompareType.GreaterThan:
                    constant = Expression.GreaterThan(left, GetRightValue<T>(condition));
                    break;
                case CompareType.GreaterThanOrEqual:
                    constant = Expression.GreaterThanOrEqual(left, GetRightValue<T>(condition));
                    break;
                case CompareType.LessThan:
                    constant = Expression.LessThan(left, GetRightValue<T>(condition));
                    break;
                case CompareType.LessThanOrEqual:
                    constant = Expression.LessThanOrEqual(left, GetRightValue<T>(condition));
                    break;
                case CompareType.Contains:
                    if (type != CompareDataType.String)
                    {
                        throw new UserFriendlyException("非字符串类型无法进行包含比较");
                    }
                    var method = typeof(string).GetMethod(nameof(string.Contains), new Type[] { typeof(string) });
                    constant = Expression.Call(left, method, GetRightValue<T>(condition));
                    break;
                case CompareType.NotContains:
                    if (type != CompareDataType.String)
                    {
                        throw new UserFriendlyException("非字符串类型无法进行不包含比较");
                    }
                    method = typeof(string).GetMethod(nameof(string.Contains), new Type[] { typeof(string) });
                    constant = Expression.Not(Expression.Call(left, method, GetRightValue<T>(condition)));
                    break;
                case CompareType.Empty:
                    if (type != CompareDataType.String)
                    {
                        throw new UserFriendlyException("非字符串类型无法进行为空比较");
                    }
                    method = typeof(string).GetMethod(nameof(string.IsNullOrEmpty), System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
                    constant = Expression.Call(method, left);
                    break;
                case CompareType.NotEmpty:
                    if (type != CompareDataType.String)
                    {
                        throw new UserFriendlyException("非字符串类型无法进行不为空比较");
                    }
                    method = typeof(string).GetMethod(nameof(string.IsNullOrEmpty), System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
                    constant = Expression.Not(Expression.Call(method, left));
                    break;
                case CompareType.HasValue:
                    constant = Expression.NotEqual(left, Expression.Constant(null));
                    break;
                case CompareType.NotHasValue:
                    constant = Expression.Equal(left, Expression.Constant(null));
                    break;
                case CompareType.NotEquals:
                    constant = Expression.NotEqual(left, GetRightValue<T>(condition));
                    break;
                case CompareType.Equals:
                default:
                    constant = Expression.Equal(left, GetRightValue<T>(condition));
                    break;
            }
            var lambda = Expression.Lambda<Func<T, bool>>(constant, parameter);
            return lambda;
        }
        catch (Exception ex)
        {
            if (ex is UserFriendlyException)
            {
                throw;
            }
            throw new UserFriendlyException("无法构建查询条件", innerException: ex);
        }
    }

    private static Expression GetRightValue<T>(DynamicConditionValue condition)
    {
        if (condition.Value == null)
        {
            throw new UserFriendlyException("值不能为空");
        }
        return condition.CompareDataType switch
        {
            CompareDataType.Int => int.TryParse(condition.Value, out var intValue) ? Expression.Constant(intValue) : throw new UserFriendlyException("值有误，无法构建查询条件"),
            CompareDataType.Long => long.TryParse(condition.Value, out var longValue) ? Expression.Constant(longValue) : throw new UserFriendlyException("值有误，无法构建查询条件"),
            CompareDataType.Double => double.TryParse(condition.Value, out var doubleValue) ? Expression.Constant(doubleValue) : throw new UserFriendlyException("值有误，无法构建查询条件"),
            CompareDataType.Float => float.TryParse(condition.Value, out var floatValue) ? Expression.Constant(floatValue) : throw new UserFriendlyException("值有误，无法构建查询条件"),
            CompareDataType.Decimal => decimal.TryParse(condition.Value, out var decimalValue) ? Expression.Constant(decimalValue) : throw new UserFriendlyException("值有误，无法构建查询条件"),
            CompareDataType.Bool => bool.TryParse(condition.Value, out var boolValue) ? Expression.Constant(boolValue) : throw new UserFriendlyException("值有误，无法构建查询条件"),
            CompareDataType.DateTime => DateTime.TryParse(condition.Value, out var dateTimeValue) ? Expression.Constant(dateTimeValue) : throw new UserFriendlyException("值有误，无法构建查询条件"),
            CompareDataType.Enum => int.TryParse(condition.Value, out var intValue) ? GetEnumRightValue<T>(condition, intValue) : throw new UserFriendlyException("值有误，无法构建查询条件"),
            CompareDataType.DataDictionary => Guid.TryParse(condition.Value, out var guidValue) ? Expression.Constant(guidValue) : throw new UserFriendlyException("值有误，无法构建查询条件"),
            _ => Expression.Constant(condition.Value),
        };
    }

    private static Expression GetEnumRightValue<T>(DynamicConditionValue condition, int intValue)
    {
        var property = typeof(T).GetProperty(condition.Field);
        var value = Enum.ToObject(property.PropertyType, intValue);
        return Expression.Constant(value);
    }
}
